﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PTA.Db;

namespace PTA.TreeHelper
{
    public class CommonTreeDal<N> : ITreeDal<N>
             where N : BaseTreeNode, new()
    {
        private string table = string.Empty;
        private IDataAccess db = null;

        public CommonTreeDal(IDataAccess db)
        {
            this.db = db;
            if (Reflect.HasAttribute<TableAttribute>(typeof(N)))
            {
                table = Reflect.GetAttribute<TableAttribute>(typeof(N)).Name;
            }
            else
            {
                table = typeof(N).Name;
            }
        }

        public virtual N Add(N node)
        {
            //查找父节点或最大的一个节点
            string sql = string.Format(@"
            IF {0} <> 0
	            SELECT ParentList,  OrderId + ChildCount + 1 OrderId,NodeLevel + 1 NodeLevel,OwnerId
	            FROM {0} WHERE OwnerId = {2} AND NodeId = {1}
            ELSE
	            SELECT '0' ParentList, OrderId + 1 OrderId ,0 NodeLevel,OwnerId
	            FROM {0} WHERE OwnerId = {2} AND OrderId = (SELECT MAX(OrderId) FROM {0} WHERE OwnerId = {2})
            ", this.table, node.ParentId, node.OwnerId);

            using (var dr = db.ExecuteReader(sql))
            {
                if (dr.Read())
                {
                    node.NodeLevel = dr.GetInt32("NodeLevel");
                    node.OrderId = dr.GetInt32("OrderId");
                    node.ParentList = Convert.FromStrictString<int>(dr.GetString("ParentList"));
                    node.OwnerId = dr.GetInt32("OwnerId");
                    if (node.ParentId != 0)
                    {
                        node.ParentList.Add(node.ParentId);
                    }
                }
                else
                {
                    node.OrderId = 1;
                    node.ParentList = new List<int>() { 0 };
                }
            }
            using (System.Transactions.TransactionScope trans = new System.Transactions.TransactionScope())
            {
                //后面的节点排序加1
                db.ExecuteNonQuery(string.Format("UPDATE {0} SET ORDERID = ORDERID + 1 WHERE ORDERID >={1} AND OWNERID = {2}", table, node.OrderId, node.OwnerId));
                //插入待插入的节点
                db.Insert(node);
                //所有父节点的子节点数加1
                db.ExecuteNonQuery(string.Format("UPDATE {0} SET ChildCount = ChildCount + 1 WHERE {1}", table, db.BuildInSql("NodeId", node.ParentList)));
                trans.Complete();
            }
            return node;
        }

        public virtual void Delete(int nid)
        {
            var node = db.ExecuteEntity<N>(string.Format("SELECT NodeId, NodeLevel, OrderId, ParentId, ChildCount, OwnerId, ParentList FROM {0} WHERE NodeId = {1}", table, nid));
            if (node != null)
            {
                StringBuilder sqls = new StringBuilder();
                sqls.AppendFormatLine("DELETE FROM {0} WHERE ORDERID BETWEEN {1} AND {2} AND OwnerId = {3};", table, node.OrderId, node.OrderId + node.ChildCount, node.OwnerId);
                sqls.AppendFormatLine("UPDATE {0} SET ORDERID = ORDERID - {1} WHERE ORDERID > {2} AND  OwnerId = {3};", table, 1 + node.ChildCount, node.OrderId + node.ChildCount, node.OwnerId);
                sqls.AppendFormatLine("UPDATE {0} SET ChildCount = ChildCount - {1} WHERE {2} AND OwnerId = {3};", table, node.ChildCount + 1, db.BuildInSql("NodeId", node.ParentList), node.OwnerId);
                db.ExecuteNonQuery(sqls.ToString());
            }
        }

        public virtual void Move(int nid, MoveDirection direction)
        {
            N vrnode = db.ExecuteEntity<N>(string.Format("SELECT NodeId, NodeLevel, OrderId, ParentId, ChildCount, OwnerId, ParentList FROM {0} WHERE NodeId = {1}", table, nid));
            N targernode = null;
            StringBuilder sqls = new StringBuilder();
            switch (direction)
            {

                case MoveDirection.向左移动:
                    {
                        #region 左移
                        //当节点的父节点不是根节点时,才左移
                        if (vrnode.ParentId != 0)
                        {
                            //获取这个节点的父节点
                            var parent = db.ExecuteEntity<N>(string.Format("SELECT NodeId, NodeLevel, OrderId, ParentId, ChildCount, OwnerId, ParentList FROM {0} WHERE NodeId = {1}", table, vrnode.ParentId));
                            #region 修正ParentList列
                            List<N> childNodes = GetNodeList(vrnode.OwnerId, new List<int>(vrnode.NodeId));
                            foreach (N node in childNodes)
                            {
                                node.ParentList.Remove(vrnode.ParentId);
                                sqls.AppendFormatLine("UPDATE {0} SET NodeLevel = NodeLevel - 1,ParentList={1}, ParentId = {2} WHERE NodeId={3};", this.table, db.ToSqlValue(node.ParentList), parent.ParentId, node.NodeId);
                            }
                            #endregion

                            //当前节点不是父节点的最后一个节点
                            if (parent.OrderId + parent.ChildCount > vrnode.OrderId + vrnode.ChildCount)
                            {
                                //当前节点的父节点所有子节点的数量减少
                                sqls.AppendFormatLine("UPDATE {0} SET ChildCount = ChildCount - {1} WHERE {2} AND OWNERID = {3};", table, vrnode.ChildCount + 1, db.BuildInSql("NodeId", vrnode.ParentList), vrnode.OwnerId);

                                //设置当前节点及所有子节点的状态
                                sqls.AppendFormatLine("UPDATE {0} SET ORDERID = ORDERID - 2100000000 WHERE ORDERID BETWEEN {1} AND {2} AND OWNERID = {3};", table, vrnode.OrderId, vrnode.OrderId + vrnode.ChildCount, vrnode.OwnerId);

                                //这个节点后面的同级节点向前移动
                                sqls.AppendFormatLine("UPDATE {0} SET ORDERID = ORDERID -{1} WHERE ORDERID BETWEEN {2} AND {3} AND OWNERID = {4};", table, vrnode.ChildCount, vrnode.OrderId + vrnode.ChildCount + 1, parent.OrderId + parent.ChildCount, vrnode.OwnerId);

                                //修正当前节点的OrderId
                                sqls.AppendFormatLine("UPDATE {0} SET ORDERID = ORDERID + 2100000000 + {1} WHERE ORDERID < 0 AND OWNERID = {2};", table, vrnode.OrderId, parent.OrderId + parent.ChildCount - (vrnode.OrderId + vrnode.ChildCount), vrnode.OwnerId);
                            }
                            db.ExecuteNonQuery(sqls.ToString());
                        }
                        break;
                        #endregion
                    }
                case MoveDirection.向右移动:
                    {
                        #region 右移
                        //找到这个节点的上一个同级节点,变为它的子节点
                        using (var sr = db.ExecuteScalar(string.Format("SELECT NodeId FROM {0} WHERE PARENTID = {1} AND NodeLevel = {2} AND ORDERID < {3} AND OWNERID = {4} ORDER BY ORDERID DESC", table, vrnode.ParentId, vrnode.NodeLevel, vrnode.OrderId, vrnode.OwnerId)))
                        {
                            if (!sr.IsNullOrDbNull)//如果没有找到,不右移
                            {
                                #region 修正ParentList列
                                List<N> childNodes = GetNodeList(vrnode.OwnerId, new List<int>(new int[] { nid }), false, true);
                                foreach (N node in childNodes)
                                {
                                    node.ParentList.Insert(vrnode.NodeLevel + 1, sr.IntValue);
                                    sqls.AppendFormatLine("UPDATE {0} SET ParentList={1},NodeLevel = NodeLevel + 1 WHERE NodeId={2};", this.table, db.ToSqlValue(node.ParentList), node.NodeId);
                                }
                                #endregion
                                sqls.AppendFormatLine("UPDATE {0} SET PARENTID = {1} WHERE NodeId = {2};", table, sr.Value, vrnode.NodeId);
                                sqls.AppendFormatLine("UPDATE {0} SET ChildCount = ChildCount + {1} WHERE NodeId = {2};", table, vrnode.ChildCount + 1, sr.Value);
                                db.ExecuteNonQuery(sqls.ToString());
                            }
                        }
                        break;
                        #endregion
                    }
                default:
                    {
                        #region 同级别移动
                        string sql = string.Format("SELECT NodeId,OrderId,ParentList,ChildCount FROM {0} WHERE ParentId = {1} AND OwnerId = {2} AND OrderId {3} {4} ORDER BY ORDERID {5}",
                            table,
                            vrnode.ParentId,
                            vrnode.OwnerId,
                            direction == MoveDirection.上移至顶 || direction == MoveDirection.向上移动 ? "<" : ">",
                            vrnode.OrderId,
                            direction == MoveDirection.向上移动 || direction == MoveDirection.下移至底 ? "DESC" : "ASC");

                        targernode = db.ExecuteEntity<N>(sql);
                        if (targernode != null)
                        {
                            //当前要移动的节点设置状态(把OrderId改变为负值)
                            sqls.AppendFormatLine("UPDATE {0} SET ORDERID = ORDERID - 2100000000 WHERE ORDERID BETWEEN {1} AND {2} AND OWNERID = {3};", table, vrnode.OrderId, vrnode.OrderId + vrnode.ChildCount, vrnode.OwnerId);

                            if (direction == MoveDirection.上移至顶 || direction == MoveDirection.向上移动)
                            {
                                //上面所有要移动的节点+当前节点的所有子节点的数量
                                sqls.AppendFormatLine("UPDATE {0} SET ORDERID = ORDERID + {1} WHERE ORDERID BETWEEN {2} AND {3} AND OWNERID = {4};", table, vrnode.ChildCount + 1, targernode.OrderId, vrnode.OrderId - 1, vrnode.OwnerId);
                                sqls.AppendFormatLine("UPDATE {0} SET ORDERID = ORDERID + 2100000000 - {1} WHERE OrderId < 0 AND OwnerId = {2};", table, vrnode.OrderId - targernode.OrderId, vrnode.OwnerId);
                            }
                            else
                            {
                                //下面所有要移动的节点-当前节点的所有子节点的数量
                                sqls.AppendFormatLine("UPDATE {0} SET ORDERID = ORDERID - {1} WHERE ORDERID BETWEEN {2} AND {3} AND OWNERID = {4};", table, vrnode.ChildCount + 1, vrnode.ChildCount + vrnode.OrderId + 1, targernode.OrderId + targernode.ChildCount, vrnode.OwnerId);
                                sqls.AppendFormatLine("UPDATE {0} SET ORDERID = ORDERID + 2100000000 + {1} WHERE OrderId < 0 AND OwnerId = {2};", table, targernode.OrderId + targernode.ChildCount - (vrnode.OrderId + vrnode.ChildCount), vrnode.OwnerId);
                            }
                            db.ExecuteNonQuery(sqls.ToString());
                        }
                        break;
                        #endregion
                    }
            }
        }

        public virtual N GetNode(int nid)
        {
            return db.ExecuteEntity<N>(string.Format("SELECT * FROM {0} WHERE NODEID = {1}", table, nid));
        }

        public virtual List<N> GetNodeList(int ownerId, int level = 0)
        {
            string sql = string.Format("SELECT * FROM {0} WHERE OWNERID={1} {2} ORDER BY ORDERID", table, ownerId, level > 0 ? "AND NodeLevel <= " + level : string.Empty);
            return new List<N>(db.ExecuteEntityArray<N>(sql));
        }

        public virtual List<N> GetNodeList(int ownerId, List<int> nodes, bool parent = false, bool child = true, int level = 0)
        {

            List<N> result = new List<N>();
            if (nodes.Count == 0)
            {
                return result;
            }
            StringBuilder sbsql = new StringBuilder("SELECT * FROM ");
            sbsql.Append(this.table);
            sbsql.AppendFormat(" WHERE OwnerId = {0} AND (", ownerId);
            #region 获取子节点和父节点的信息
            List<int> parents = new List<int>();
            List<N> tmpnodes = db.ExecuteEntityArray<N>(string.Format("SELECT NodeId,OrderId,ChildCount,OwnerId FROM {0} WHERE OwnerId = {1} AND {2}", table, ownerId, db.BuildInSql("NodeId", ownerId, nodes)));
            bool hasor = false;
            foreach (var node in tmpnodes)
            {
                if (child)
                {
                    sbsql.AppendFormat("{0} ( ORDERID BETWEEN {1} AND {2})", hasor ? " OR " : string.Empty, node.OrderId, node.OrderId + node.ChildCount);
                    hasor = true;
                }
                if (parent)
                {
                    node.ParentList.ForEach(item => { if (!parents.Contains(item)) { parents.Add(item); } });
                }
            }
            #endregion
            if (parents.Count > 0)
            {
                sbsql.AppendFormat(" OR {0}", db.BuildInSql("NodeId", parents));
            }
            sbsql.Append(" )");
            if (level != 0)
            {
                sbsql.AppendFormat(" AND NodeLevel >= {0}", level);
            }
            sbsql.Append(" ORDER BY OrderId");
            result.AddRange(db.ExecuteEntityArray<N>(sbsql.ToString()));
            return result;
        }

        public virtual List<N> GetNodeList(int ownerId, int nid, bool parent = false, bool child = true, int level = 0)
        {
            List<N> result = new List<N>();
            var curnode = db.ExecuteEntity<N>(string.Format("SELECT NodeId,OrderId,ChildCount,OwnerId FROM {0} WHERE NodeId ={1}", table, nid));
            if (curnode != null)
            {

                StringBuilder sbsql = new StringBuilder("SELECT * FROM ");
                sbsql.Append(this.table);
                sbsql.AppendFormat(" WHERE OwnerId = {0} AND (", ownerId);
                #region 获取子节点和父节点的信息
                if (child)
                {
                    sbsql.AppendFormat(" ( ORDERID BETWEEN {0} AND {1})", curnode.OrderId, curnode.OrderId + curnode.ChildCount);
                }
                if (parent)
                {
                    sbsql.AppendFormat(" OR {0}", db.BuildInSql("NodeId", curnode.ParentList));
                }
                #endregion
                sbsql.Append(" )");
                if (level != 0)
                {
                    sbsql.AppendFormat(" AND NodeLevel >= {0}", level);
                }
                sbsql.Append(" ORDER BY OrderId");
                result.AddRange(db.ExecuteEntityArray<N>(sbsql.ToString()));
            }
            return result;
        }
    }
}
